package cn.richard.demo.tax.config;

import java.util.List;

import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.StatelessKieSession;
import org.kie.internal.io.ResourceFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import cn.hutool.core.collection.CollUtil;
import cn.richard.demo.tax.entity.DroolsRule;
import cn.richard.demo.tax.service.DroolsRuleService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class KieUtil {
	//指定规则文件存放的目录
    private static final String RULES_PATH = "rules/";
    
	public final static KieServices kieServices = KieServices.Factory.get();
	
	private static String active;
	
	private static KieContainer kieContainer;
	
	private static DroolsRuleService droolsRuleService;
	
//	private static final Object PRESENT = new Object();
	
    public static void setKieContainer(KieContainer kieContainer) {
    	KieUtil.kieContainer = kieContainer;
    }

    public static void setDroolsRuleService(DroolsRuleService droolsRuleService) {
    	KieUtil.droolsRuleService = droolsRuleService;
    }
    
    public static void setActive(String active) {
    	KieUtil.active = active;
    }
    
    /**
     * 触发规则<br>
     * 默认的 kieSession 线程安全化使用
     * @see StatelessKieSession
     * @param facts
     */
    public static void fire(Object fact) {
    	log.info(">> fire rules");
		KieSession kieSession = kieContainer.newKieSession();
		try {
			log.info("fire 前 fact 状态：{}", fact.toString());
			kieSession.insert(fact);
			int count = kieSession.fireAllRules();
			log.info("本次触发了 {} 条规则", count);
			log.info("fire 后 fact 状态：{}", fact.toString());
		} finally {
			if (kieSession != null) {
				//??? >> https://docs.jboss.org/drools/release/7.42.0.Final/drools-docs/html_single/index.html#performance-tuning-decision-engine-ref_decision-engine
				//ksession.detachListener(listener); 
				kieSession.dispose();
			}
		}
    	log.info("<< fire rules");
    }

    /**
     * 重载规则
     */
    public static void reload() {
    	log.info(">> reload rules");
		KieFileSystem kfs = getKieFileSystem();	
		KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();
		//语法校验
		Results results = kieBuilder.getResults();
		if (results.hasMessages(Message.Level.ERROR)) {
			log.error("drl语法错误：{}", results.getMessages().get(0).toString());
			throw new IllegalStateException(results.getMessages().get(0).getText());
		}
		//KIE-API实例重置
		KieContainer newKieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
		KieUtil.setKieContainer(newKieContainer);
        log.info("<< reload rules");
    }
    
    public static KieFileSystem getKieFileSystem(){
    	KieFileSystem kfs = kieServices.newKieFileSystem();
    	if("local".equals(active)) {
    		log.info("{}环境，从类路径下加载和刷新规则", active);
    		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    		try {
    			Resource[] files = resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "*.drl");
    			String path = null;
    			for (Resource file : files) {
    				path = RULES_PATH + file.getFilename();
    				kfs.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
    			}
			} catch (Exception e) {
				log.error("类路径下rule目录drl文件读取异常", e);
				throw new RuntimeException("类路径下rule目录drl文件读取异常", e);
			}
    	}else {
    		log.info("{}环境，从数据库类加载和刷新规则", active);
//    		List<DroolsRule> list = droolsRuleService.listAll();
    		List<DroolsRule> list = droolsRuleService.list();
    		if(CollUtil.isEmpty(list)) {
    			throw new RuntimeException("未找到相关规则定义");
    		}
    		//TODO 有木有不需要临时文件的动态刷新方案 -> 有，使用KieHelper，见下一个demo
    		list.forEach(o -> {
    			String drlFileClassPath = String.format("src/main/resources/%s%s-%d.drl", RULES_PATH, o.getRuleFor(), o.getId());
    			kfs.write(drlFileClassPath, o.getDrlContent());
    		});
    	}
    	
        return kfs;
    }
}
